package main

import (
	"compress/gzip"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/gin-gonic/gin"
)

type Config struct {
	ListenPort int `json:"listenPort"`
}

var config Config

func init() {
	// 获取当前执行文件的路径
	ex, err := os.Executable()
	if err != nil {
		log.Fatalf("无法获取执行文件路径: %v", err)
	}
	exPath := filepath.Dir(ex)

	// 构建配置文件的绝对路径
	configPath := filepath.Join(exPath, "config.json")
	log.Printf("尝试读取配置文件: %s", configPath)

	// 读取配置文件
	file, err := os.Open(configPath)
	if err != nil {
		// 如果找不到文件，尝试在当前工作目录查找
		cwd, _ := os.Getwd()
		configPath = filepath.Join(cwd, "config.json")
		log.Printf("尝试在当前工作目录读取配置文件: %s", configPath)
		file, err = os.Open(configPath)
		if err != nil {
			log.Fatalf("无法打开配置文件: %v", err)
		}
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		log.Fatalf("无法解析配置文件: %v", err)
	}

	log.Printf("已成功加载配置: %+v", config)
}

func main() {
	r := gin.Default()
	r.GET("/proxy/*proxyPath", proxyRequest)
	r.Run(fmt.Sprintf(":%d", config.ListenPort))
}

func proxyRequest(c *gin.Context) {
	proxyPath := c.Param("proxyPath")
	log.Printf("收到代理请求: %s", proxyPath)

	// 移除开头的斜杠
	targetURL := strings.TrimPrefix(proxyPath, "/")

	// 处理双斜杠的情况
	if strings.HasPrefix(targetURL, "/") {
		targetURL = "http:/" + targetURL
	} else if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
		targetURL = "http://" + targetURL
	}

	log.Printf("目标 URL: %s", targetURL)

	// 解析 URL
	parsedURL, err := url.Parse(targetURL)
	if err != nil {
		c.String(http.StatusBadRequest, "无效的 URL: %v", err)
		return
	}

	// 确保 URL 有 Host
	if parsedURL.Host == "" {
		c.String(http.StatusBadRequest, "URL 中没有 Host")
		return
	}

	// 保存原始主机信息
	originalHost := parsedURL.Host

	req, err := http.NewRequest(c.Request.Method, parsedURL.String(), c.Request.Body)
	if err != nil {
		c.String(http.StatusInternalServerError, "创建请求失败: %v", err)
		return
	}

	// 复制原始请求的头部
	for name, values := range c.Request.Header {
		for _, value := range values {
			req.Header.Add(name, value)
		}
	}

	// 对于POST请求，确保设置正确的Content-Type
	if c.Request.Method == "POST" {
		req.Header.Set("Content-Type", c.ContentType())
	}

	// 明确接受gzip编码
	req.Header.Set("Accept-Encoding", "gzip")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		c.String(http.StatusInternalServerError, "请求目标服务器失败: %v", err)
		return
	}
	defer resp.Body.Close()

	// 读取响应体
	var bodyBytes []byte
	var reader io.Reader = resp.Body

	if resp.Header.Get("Content-Encoding") == "gzip" {
		gzipReader, err := gzip.NewReader(resp.Body)
		if err != nil {
			c.String(http.StatusInternalServerError, "解压gzip内容失败: %v", err)
			return
		}
		defer gzipReader.Close()
		reader = gzipReader
	}

	bodyBytes, err = io.ReadAll(reader)
	if err != nil {
		c.String(http.StatusInternalServerError, "读取响应内容失败: %v", err)
		return
	}

	// 修改HTML内容
	contentType := resp.Header.Get("Content-Type")
	if strings.Contains(contentType, "text/html") {
		bodyBytes = modifyHTML(bodyBytes, c.Request.Host, originalHost)
	}

	// 设置响应头
	for name, values := range resp.Header {
		for _, value := range values {
			c.Header(name, value)
		}
	}

	// 移除Content-Length头，因为内容可已经被修改
	c.Header("Content-Length", "")

	// 如果原始响应是gzip压缩的，我们需要重新压缩
	if resp.Header.Get("Content-Encoding") == "gzip" {
		var buf bytes.Buffer
		gzipWriter := gzip.NewWriter(&buf)
		_, err := gzipWriter.Write(bodyBytes)
		if err != nil {
			c.String(http.StatusInternalServerError, "压缩响应内容失败: %v", err)
			return
		}
		gzipWriter.Close()
		bodyBytes = buf.Bytes()
	} else {
		// 如果原始响应不是gzip压缩的，移除Content-Encoding头
		c.Header("Content-Encoding", "")
	}

	// 设置状态码并写入响应体
	c.Data(resp.StatusCode, contentType, bodyBytes)
}

func modifyHTML(body []byte, proxyHost, originalHost string) []byte {
	proxyURL := fmt.Sprintf("http://%s/proxy/", proxyHost)

	// 替换绝对URL（包括src, href, action属性）
	re := regexp.MustCompile(`(src|href|action)=['"](https?:)?//([^'"]+)['"']`)
	body = re.ReplaceAllFunc(body, func(match []byte) []byte {
		parts := re.FindSubmatch(match)
		if len(parts) == 4 {
			attr := string(parts[1])
			protocol := string(parts[2])
			if protocol == "" {
				protocol = "http:"
			}
			url := string(parts[3])
			return []byte(fmt.Sprintf(`%s="%s%s//%s"`, attr, proxyURL, protocol, url))
		}
		return match
	})

	// 替换相对URL（包括src, href, action属性）
	re = regexp.MustCompile(`(src|href|action)=['"](/[^'"]*)['"']`)
	body = re.ReplaceAllFunc(body, func(match []byte) []byte {
		parts := re.FindSubmatch(match)
		if len(parts) == 3 {
			attr := string(parts[1])
			path := string(parts[2])
			return []byte(fmt.Sprintf(`%s="%s%s"`, attr, proxyURL, path))
		}
		return match
	})

	// 替换JavaScript中的URL字符串
	re = regexp.MustCompile(`(['"])(https?:?//[^'"]+)(['"])`)
	body = re.ReplaceAllFunc(body, func(match []byte) []byte {
		parts := re.FindSubmatch(match)
		if len(parts) == 4 {
			url := string(parts[2])
			return []byte(fmt.Sprintf(`%s%s%s%s`, parts[1], proxyURL, url, parts[3]))
		}
		return match
	})

	// 替换JavaScript中的相对路径
	re = regexp.MustCompile(`(['"])(/[^'"]+)(['"])`)
	body = re.ReplaceAllFunc(body, func(match []byte) []byte {
		parts := re.FindSubmatch(match)
		if len(parts) == 4 {
			path := string(parts[2])
			return []byte(fmt.Sprintf(`%s%s%s%s`, parts[1], proxyURL, path, parts[3]))
		}
		return match
	})

	return body
}